package database

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"os"
	"time"

	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

type Database interface {
	// Open 打开数据库连接
	Open() error
	// Close 关闭数据库连接
	Close() error
	// Migrate 迁移数据库
	Migrate(dst ...interface{}) error
	// GetDB 获取数据库对象
	GetDB() *gorm.DB
}

type databaseImpl struct {
	logger logger.Interface // 日志对象
	driver driverType       // 驱动类型
	dsn    string           // 连接字符串
	opened bool             // 是否已经打开
	db     *gorm.DB         // 数据库对象
	sqlDB  *sql.DB          // sql数据库对象
}

func NewSQLiteDB(dbPath string, printSql bool) (Database, error) {
	return newDB(dbPath, driver_sqlite_type, printSql), nil
}

func NewPostgresDB(config *DatabaseConfig, printSql bool) (Database, error) {
	// 生成dsn
	dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai",
		config.Host,
		config.Username,
		config.Password,
		config.DbName,
		config.Port,
	)
	return newDB(dsn, driver_postgres_type, printSql), nil
}

func NewMySQLDB(config *DatabaseConfig, printSql bool) (Database, error) {
	// 生成dsn
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		config.Username,
		config.Password,
		config.Host,
		config.Port,
		config.DbName,
	)
	return newDB(dsn, driver_mysql_type, printSql), nil
}

func newDB(dsn string, driver driverType, printSql bool) Database {
	return &databaseImpl{
		logger: generateLogger(printSql),
		driver: driver,
		dsn:    dsn,
		opened: false,
		db:     nil,
		sqlDB:  nil,
	}
}

func generateLogger(printSql bool) logger.Interface {
	if !printSql {
		return nil
	}
	return logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer（日志输出到标准输出）
		logger.Config{
			SlowThreshold:             time.Second, // 慢 SQL 阈值
			LogLevel:                  logger.Info, // 日志级别
			IgnoreRecordNotFoundError: true,        // 忽略ErrRecordNotFound错误
			Colorful:                  true,        // 禁用/启用彩色打印
		},
	)
}

func (d *databaseImpl) Open() error {
	if d == nil {
		return errors.New("Database is nil")
	}
	if d.opened {
		return nil
	}
	switch d.driver {
	case driver_mysql_type:
		return d.openMySQLDB()
	case driver_postgres_type:
		return d.openPostgresDB()
	case driver_sqlite_type:
		return d.openSQLiteDB()
	default:
		return errors.New("Unsupported driver type")
	}
}

func (d *databaseImpl) Close() error {
	if d == nil {
		return errors.New("Database is nil")
	}
	if !d.opened {
		return nil
	}
	if err := d.sqlDB.Close(); err != nil {
		return err
	}
	d.opened = false
	return nil
}

func (d *databaseImpl) Migrate(dst ...interface{}) error {
	if d == nil {
		return errors.New("Database is nil")
	}
	return d.db.AutoMigrate(dst...)
}

func (d *databaseImpl) GetDB() *gorm.DB {
	if d == nil {
		return nil
	}
	return d.db
}
